/*!
    \file    main.c
    \brief   SPI LCD demo

    \version 2025-08-08, V1.1.0, demo for gd32c2x1
*/

/*
    Copyright (c) 2025, GigaDevice Semiconductor Inc.

    Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this
       list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright notice,
       this list of conditions and the following disclaimer in the documentation
       and/or other materials provided with the distribution.
    3. Neither the name of the copyright holder nor the names of its contributors
       may be used to endorse or promote products derived from this software without
       specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

#include "gd32c2x1.h"
#include "systick.h"
#include "lcd_driver.h"
#include "tft_test.h"
#include "gd32c231c_eval.h"
// 新增
#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "eemul_port.h"
#include "bits_button.h"

void test_status_led_init(void);
void flash_led(uint32_t times);

//新增
static void lcd_task(void *pvParameters);
static void led_task(void *pvParameters);

// 静态任务控制块（TCB）：存储任务状态（优先级、栈指针、任务句柄等），静态分配时需手动声明
static StaticTask_t lcdTaskTCB;
static StaticTask_t ledTaskTCB;

// 静态任务栈：任务运行时的栈空间，静态分配（从RAM中固定地址分配）
static StackType_t lcdTaskStack[512]; // LCD任务栈：512个StackType_t（默认4字节/个 → 2KB）
static StackType_t ledTaskStack[configMINIMAL_STACK_SIZE]; // LED任务栈：使用FreeRTOS最小栈大小配置

static TimerHandle_t bits_btn_timer;
static StaticTimer_t bits_btn_timer_buffer;
static void bits_btn_timer_callback(TimerHandle_t xTimer);

/* BitsButton按键相关变量 */
volatile uint8_t g_key_pressed = 0; // 区分不同按键按下的变量

/* BitsButton按键定义 */
typedef enum
{
    USER_BUTTON_0 = 0,
    USER_BUTTON_1,
    USER_BUTTON_2,
    USER_BUTTON_3,
    USER_BUTTON_4,
    USER_BUTTON_5,
    USER_BUTTON_6,
    USER_BUTTON_7,
    USER_BUTTON_8,
    USER_BUTTON_9,
    USER_BUTTON_INVALID,
    USER_BUTTON_MAX,

    USER_BUTTON_COMBO_0 = 0x100,
    USER_BUTTON_COMBO_1,
    USER_BUTTON_COMBO_2,
    USER_BUTTON_COMBO_3,
    USER_BUTTON_COMBO_MAX,
} user_button_t;

/* BitsButton按键参数和实例 */
static const bits_btn_obj_param_t defaul_param = {
    .long_press_period_triger_ms = BITS_BTN_LONG_PRESS_PERIOD_TRIGER_MS,
    .long_press_start_time_ms = BITS_BTN_LONG_PRESS_START_TIME_MS,
    .short_press_time_ms = BITS_BTN_SHORT_TIME_MS,
    .time_window_time_ms = BITS_BTN_TIME_WINDOW_TIME_MS};

button_obj_t btns[] = {
    BITS_BUTTON_INIT(USER_BUTTON_0, 1, &defaul_param), /* KEY_WAKEUP - 高电平有效 */
    BITS_BUTTON_INIT(USER_BUTTON_1, 0, &defaul_param), /* KEY_USER - 低电平有效 */
    // BITS_BUTTON_INIT(USER_BUTTON_2, 1, &defaul_param),
};

button_obj_combo_t btns_combo[] =
    {
        BITS_BUTTON_COMBO_INIT(
            USER_BUTTON_COMBO_0,                          // 组合键ID
            1,                                            // 有效电平
            &defaul_param,                                // 参数配置
            ((uint16_t[]){USER_BUTTON_0, USER_BUTTON_1}), // 组合按键成员
            2,                                            // 组合键成员数量
            1),                                           // 抑制单键事件
};

/* BitsButton按键状态读取函数 */
uint8_t read_key_state(struct button_obj_t *btn)
{
    uint8_t id = btn->key_id;
    // 可以让多个按钮共享GPIO读取功能
    switch (id)
    {
    case USER_BUTTON_0:
        return gd_eval_key_state_get(KEY_WAKEUP); // 需要自行实现
        break;
    case USER_BUTTON_1:
        return gd_eval_key_state_get(KEY_USER);
        break;
    default:
        return 0;
        break;
    }
    return 0;
}

/* BitsButton按键结果回调函数 */
void bits_btn_result_cb(struct button_obj_t *btn, struct bits_btn_result result)
{
    printf("id:%d, event:%d, key_value:%d, long press period trigger cnt:%d \r\n",
           result.key_id, result.event, result.key_value, result.long_press_period_trigger_cnt);

    /* 处理按键事件 */
    if (result.event == BTN_STATE_PRESSED)
    {
        printf("id:%d pressed \n", result.key_id);
    }

    if (result.event == BTN_STATE_RELEASE)
    {
        printf("id:%d release\n", result.key_id);
    }

    if (result.event == BTN_STATE_LONG_PRESS)
    {
        if (result.key_value == 0b11)
            printf("id:%d, long press start\n", result.key_id);
        else if (result.key_value == 0b111)
        {
            printf("id:%d, long press hold, cnt:%d\n", result.key_id, result.long_press_period_trigger_cnt);
        }
        else if (result.key_value == 0b1011)
            printf("id:%d, single click and long press start\n", result.key_id);
        else if (result.key_value == 0b101011)
            printf("id:%d, double click and long press start\n", result.key_id);
    }

    if (result.event == BTN_STATE_FINISH)
    {
        switch (result.key_value)
        {
        case BITS_BTN_SINGLE_CLICK_KV:
            printf("id:%d single click\n", result.key_id);
            /* 映射到原有的按键处理 */
            if (result.key_id == USER_BUTTON_0)
            {
                g_key_pressed = 1; /* WAKEUP_KEY */
            }
            else if (result.key_id == USER_BUTTON_1)
            {
                g_key_pressed = 2; /* USER_KEY */
            }
            break;
        case BITS_BTN_DOUBLE_CLICK_KV:
            printf("id:%d double click", result.key_id);
            break;
        case 0b10111010:
            printf("id:%d single click and long press then single click\n", result.key_id);
            break;
        }
    }
    // 通用的长按保持处理（不同的方式判别长按保持）
    if (result.event == BTN_STATE_LONG_PRESS && result.long_press_period_trigger_cnt > 0)
    {
        printf("[%d] 长按保持 周期:%d\r\n",
               result.key_id,
               result.long_press_period_trigger_cnt);
        // 长按保持处理（如连续调节音量）
    }
}

static void bits_btn_timer_callback(TimerHandle_t xTimer)
{
    bits_button_ticks();
}

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
#ifdef __FIRMWARE_VERSION_DEFINE
    uint32_t fw_ver = 0; // 固件版本号变量
#endif

#ifndef SYS_SUPPORT_OS
    /* configure the systick */
    systick_config();
#endif
    /* 初始化调试串口 */
    gd_eval_com_init(EVAL_COM);
    /* 初始化测试状态指示灯 */
    test_status_led_init();

    /* 初始化按键GPIO为输入模式 */
    gd_eval_key_init(KEY_USER, KEY_MODE_GPIO);
    gd_eval_key_init(KEY_WAKEUP, KEY_MODE_GPIO);

    int32_t ret = bits_button_init(btns, ARRAY_SIZE(btns), btns_combo, ARRAY_SIZE(btns_combo),
                                   read_key_state, bits_btn_result_cb, printf);
    if (ret != 0)
    {
        printf("\r\n   BitsButton初始化失败, ret:%d", ret);
    }
    else
    {
        printf("\r\n   BitsButton按键框架初始化完成");
        printf("\r\n   支持功能: 单击、双击、长按、长按保持");
    }

    bits_btn_timer = xTimerCreateStatic("BitsBtn",
                                        pdMS_TO_TICKS(BITS_BTN_TICKS_INTERVAL),
                                        pdTRUE,
                                        NULL,
                                        bits_btn_timer_callback,
                                        &bits_btn_timer_buffer);
    xTimerStart(bits_btn_timer, 0);

    xTaskCreateStatic(lcd_task, "LCD", sizeof(lcdTaskStack) / sizeof(StackType_t), NULL, tskIDLE_PRIORITY + 2, lcdTaskStack, &lcdTaskTCB);
    xTaskCreateStatic(led_task, "LED", sizeof(ledTaskStack) / sizeof(StackType_t), NULL, tskIDLE_PRIORITY + 1, ledTaskStack, &ledTaskTCB);
    vTaskStartScheduler(); // 启动FreeRTOS调度器：开始任务切换和执行
    while(1) {}
}

/*!
    \brief      test status led initialize
    \param[in]  none
    \param[out] none
    \retval     none
*/
void test_status_led_init(void)
{
    /* initialize the leds */
    gd_eval_led_init(LED1);
    gd_eval_led_init(LED2);
    gd_eval_led_init(LED3);
    gd_eval_led_init(LED4);

    /* close all of leds */
    gd_eval_led_off(LED1);
    gd_eval_led_off(LED2);
    gd_eval_led_off(LED3);
    gd_eval_led_off(LED4);
}

/*!
    \brief      flash leds
    \param[in]  times: leds blink times
    \param[out] none
    \retval     none
*/
void flash_led(uint32_t times)
{
    int i;

    for(i = 0; i < times; i ++) {
        vTaskDelay(pdMS_TO_TICKS(200));

        /* turn on leds */
        gd_eval_led_on(LED1);
        gd_eval_led_on(LED2);
        gd_eval_led_on(LED3);
        gd_eval_led_on(LED4);

        vTaskDelay(pdMS_TO_TICKS(200));

        /* turn off leds */
        gd_eval_led_off(LED1);
        gd_eval_led_off(LED2);
        gd_eval_led_off(LED3);
        gd_eval_led_off(LED4);
    }
}

#if (configCHECK_FOR_STACK_OVERFLOW > 0)
/* FreeRTOS 应用栈溢出钩子函数实现 - 当检测到栈溢出时调用 */
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName)
{
    /* 禁用中断以防止错误扩大 */
    taskDISABLE_INTERRUPTS();
    printf("栈溢出错误！任务名称：%s\n", pcTaskName); // 打印溢出任务名称
    /* 无限循环 - 栈溢出属于严重错误，需排查修复 */
    for( ;; );
}
#endif

#if (configSUPPORT_STATIC_ALLOCATION == 1)
/* 空闲任务内存分配函数 - 当 configSUPPORT_STATIC_ALLOCATION 配置为 1 时必需实现 */
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
                                   StackType_t **ppxIdleTaskStackBuffer,
                                   uint32_t *pulIdleTaskStackSize)
{
    /* 定义空闲任务的静态内存（TCB + 栈） */
    static StaticTask_t xIdleTaskTCB;          // 空闲任务控制块（TCB）
    static StackType_t uxIdleTaskStack[configMINIMAL_STACK_SIZE];  // 空闲任务栈
    
    /* 将静态内存地址传递给 FreeRTOS 内核 */
    *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;          // 传递TCB缓冲区地址
    *ppxIdleTaskStackBuffer = uxIdleTaskStack;      // 传递栈缓冲区地址
    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;  // 传递栈大小（单位：栈元素个数）
}

/* 定时器任务内存分配函数 - 当 configSUPPORT_STATIC_ALLOCATION 配置为 1 时必需实现 */
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
                                    StackType_t **ppxTimerTaskStackBuffer,
                                    uint32_t *pulTimerTaskStackSize)
{
    /* 定义定时器任务的静态内存（TCB + 栈） */
    static StaticTask_t xTimerTaskTCB;          // 定时器任务控制块（TCB）
    static StackType_t uxTimerTaskStack[configTIMER_TASK_STACK_DEPTH];  // 定时器任务栈
    
    /* 将静态内存地址传递给 FreeRTOS 内核 */
    *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;          // 传递TCB缓冲区地址
    *ppxTimerTaskStackBuffer = uxTimerTaskStack;      // 传递栈缓冲区地址
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;  // 传递栈大小（单位：栈元素个数）
}
#endif

static void lcd_task(void *pvParameters)
{
    lcd_init();
    for(;;) {
        lcd_clear(BLUE);
        font_test();
        num_test();
        draw_test();
        color_test();
    }
}

static void led_task(void *pvParameters)
{
    // TickType_t lastWakeTime;
    // // 初始化：获取当前时间作为第一次“唤醒时间”
    // lastWakeTime = xTaskGetTickCount();
    for(;;) {
        gd_eval_led_on(LED1);
        gd_eval_led_on(LED2);
        gd_eval_led_on(LED3);
        gd_eval_led_on(LED4);
        // vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(1000));
        vTaskDelay(pdMS_TO_TICKS(1000));
        gd_eval_led_off(LED1);
        gd_eval_led_off(LED2);
        gd_eval_led_off(LED3);
        gd_eval_led_off(LED4);
        // vTaskDelayUntil(&lastWakeTime, pdMS_TO_TICKS(1000));
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}
